const aeslib = require('../plugins/ar-aes');
const w_md5 = require('../plugins/w_md5');
const baseConfig = require('../config/base');
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);
const Mime = require('mime');


global.json = (data = "", message = "ok", status = 200) => ({ message, status, data });



/**
 * 获取指定路径的文件大小和响应头
 * @param {string} pathstr 
 * @returns {object}
 */
global.filehead = function (pathstr) {
    const fileInfo = fs.statSync(pathstr);
    const mimeType = Mime.getType(pathstr); // 获取文件类型
    return {
        'Content-Length': fileInfo.size,
        'Content-Type': mimeType,
    }
}

/**
 * 模型快捷调用函数
 * @param {String} model 
 * @returns 
 */
global.model = (model) => {
    let modelpath = path.resolve(__dirname, './model/' + model);
    try {
        let modelsC = require(modelpath);//存入全局
        return new modelsC;//返回new好的对象
    } catch (error) {
        console.error(`${model} not found`);
        return null;
    }
};
/**
 * 快捷使用验证器
 * @param {String} validate 验证器   文件夹 /分割
 * @param {String} [scene] 验证场景
 * @returns 
 */
global.validates = (validate, scene = null) => {
    let valipath = path.resolve(__dirname, './validate/' + validate);
    try {
        let validatesC = require(valipath);//存入全局
        return validatesC(scene);//返回对象
    } catch (error) {
        console.error(`${validate} not found`);
        return null;
    }
};

/**
 * 快捷使用中间键
 * @param {string} middindex 中间键
 * @param  {...any} Param 参数列表
 * @returns 
 */
global.middindex = (middindex, ...Param) => {
    let middindexpath = path.resolve(__dirname, './middleware/' + middindex);
    try {
        let middindexC = require(middindexpath);//存入全局
        if (Param.length > 0) {
            return middindexC(...Param);//返回方法对象并调用
        }
        return middindexC;//返回对象
    } catch (error) {
        console.error(`${middindex} not found`);
        return null;
    }
};


module.exports = () => {

    class functions{


        /**
         * 输出json格式
         * @param {Any} data 输出数据
         * @param {string} message 输出提示
         * @param {int} status 输出的响应码 默认200
         * @returns 
         */
        json(data = "", message = "ok", status = 200) {
            return { message, status, data };
        }


        /**
         * 
         * @param {String} str 需要加密的字符串（其它类型会被转字符串）
         * @param {16|32} digit 位数
         * @param {Boolean} isupper 是否大写
         */
        md5(str, digit = 32, isupper = false) {
            if (typeof str === 'undefined') return null;
            if (typeof str !== 'string') {
                str = JSON.stringify(str);
            }
            if (digit == 32) {
                if (isupper) return w_md5.md5_32Upper(str);
                return w_md5.md5_32(str);
            }
            if (digit == 16) {
                if (isupper) return w_md5.md5_16Upper(str);
                return w_md5.md5_16(str);
            }
            return null;
        }

        //进行加密
        encrypt(str = '') {
            if (typeof str === 'undefined') return '';
            if (typeof str !== 'string') {
                str = JSON.stringify(str);
            }
            return aeslib.AES.encrypt(str, baseConfig.encrypt.key, baseConfig.encrypt.excursion);//进行加密
        }

        //解密
        decrypt(str = '') {
            if (typeof str === 'undefined') return '';
            if (typeof str !== 'string') return '';
            return aeslib.AES.decrypt(str, baseConfig.encrypt.key, baseConfig.encrypt.excursion);//进行解密
        }


        /**
         * 获取当前年月日
         * @param {Object} 
         * @returns {String}
         */
        getNowFormatDate(variation = {}, dateObj = null) {
            let dd = variation.dd || 0;
            let date = dateObj || new Date();
            let seperator1 = "-";
            let year = date.getFullYear();
            let month = date.getMonth() + 1;
            let strDate = date.getDate();
            if (month >= 1 && month <= 9) {
                month = "0" + month;
            }
            if (strDate >= 0 && strDate <= 9) {
                strDate = "0" + strDate;
            }
            let currentdate = year + seperator1 + month + seperator1 + strDate;
            return this.dateAddDays(currentdate, dd);
        }

        /**
         * [dateAddDays 从某个日期增加n天后的日期]
         * @param  {[string]} dateStr  [日期字符串]
         * @param  {[int]} dayCount [增加的天数]
         * @return {[string]}[增加n天后的日期字符串]
        */
        dateAddDays(dateStr, dayCount) {
            var tempDate = new Date(dateStr.replaceAll('-', "/"));//把日期字符串转换成日期格式
            var resultDate = new Date((tempDate / 1000 + (86400 * dayCount)) * 1000);//增加n天后的日期
            var resultDateStr = resultDate.getFullYear() + "-" + (resultDate.getMonth() + 1) + "-" + (resultDate.getDate());//将日期转化为字符串格式
            return resultDateStr;
        }


        /**
         * 交换新的token
         * @param {String} oldToken 旧的token
         * @returns {Object} {userid,token}
         */
        swapToken(oldToken) {
            oldToken = String(oldToken);
            let tokenS = oldToken.split('.');
            if (tokenS.length !== 3) return null;
            let jwtData = this.decrypt(tokenS[1]);
            if (!jwtData) return null;
            let jwtEnd = this.strToObj(this.decrypt(tokenS[2]));
            if (!jwtEnd) return null;
            let token = this.setToken(jwtEnd.userid, jwtData);
            return { userid: jwtEnd.userid, token }
        }


        /**
         * 获取Token存储的数据
         * @param {String} token 
         * @returns {Object|null}
         */
        getToken(token) {
            token = String(token);
            let tokenS = token.split('.');
            if (tokenS.length !== 3) return null;
            let jwtHead = this.strToObj(this.decrypt(tokenS[0]));
            if (!jwtHead) return null;
            if ((jwtHead.time + jwtHead.ttl) < Date.now()) return null;//过期了
            let jwtData = this.decrypt(tokenS[1]);
            if (!jwtData) return null;
            let jwtEnd = this.strToObj(this.decrypt(tokenS[2]));
            if (!jwtEnd) return null;
            return { userid: jwtEnd.userid, data: this.strToObj(jwtData) };
        }

        /**
         * 生成新的token
         * @param {Int} userid 用户id
         * @param {Any} value 存储的数据
         * @return {String}
         */
        setToken(userid, value = {}) {
            let ttl = 60 * 60 * 1;//token有限期1小时
            let jwtHead = this.encrypt({ time: Date.now(), ttl: (ttl * 1000) });
            let jwtData = this.encrypt(value);
            let jwtEnd = this.encrypt({ userid });
            let token = `${jwtHead}.${jwtData}.${jwtEnd}`;
            return token;
        }

        /**
         * json转对象 失败返回null
         * @param {String} json 
         */
        strToObj(json) {
            try {
                return JSON.parse(json);
            } catch (error) {
                return null;
            }
        }

        /**
         * 判断类型 
         * @param {Any} data 
         * @returns {string} 小写
         */
        isTypeof(data) {
            let type = Object.prototype.toString.call(data);
            return type.replace(/^\[object (.+)\]$/, '$1').toLowerCase();
        }

         /**
         * 获取缓存 的邮箱验证码
         * @param {String} email 
         * @param {String} key 
         * @returns 
         */
         getCode(email, key = 'reg') {
            let identify = `code_${key}_` + email;
            let code_reg = global.cache.get(identify);
            if (code_reg) {
                return code_reg.code;
            }
            return null;
        }


         /**
         * 快捷发送验证码
         * @param {String} email 邮箱地址
         * @param {String} key 标识名(reg注册,login登录)类似
         * @param {String} title 标题
         * @returns {Object}
         */
         async sendCode(email, key = 'reg', title = '验证码') {
            let identify = `code_${key}_` + email;
            let code_reg = global.cache.get(identify);
            let randCodeNum = Math.floor(Math.random() * 999999) + 100000;
            let setCode = async () => {
                let resultS = await global.sendMail(email, `
                    <div style="padding: 20px;border:1px solid #9c9c9c;background-color: white;border-radius: 15px;">
                        <div style="font-size: 20px;">验证码</div>
                        <div style="font-weight: bold;font-size: 30px;color: blue;">${randCodeNum}</div>
                        <div style="font-size: 14px;color:#333;">验证码5分钟内有效,请勿泄露您的验证码!</div>
                    </div>
                    `, title);
                if (resultS.status) {
                    // 发送成功才设置缓存
                    global.cache.set(identify, { code: randCodeNum, time: Date.now() }, 300);
                }
                return resultS;
            };
            if (code_reg) {
                let remainTiem = (Date.now() - code_reg.time);
                if (remainTiem > 60000) {
                    return setCode();
                }
                return { status: false, msg: ('发送频繁,稍等再试!' + (60 - parseInt(remainTiem / 1000))) };
            }
            return setCode();
        }


    }

    class ctxBase extends functions {

        #ctx = null;
        host = 'http://127.0.0.1:8000';//全局host配置

        constructor(ctx) {
            super();
            this.#ctx = ctx;
            this.host = baseConfig.host ?? this.host;
        }

        /**
          * 获取客户端ip
          * @returns 
          */
        getClientIp() {
            let api = this.#ctx.request.ip;
            if (String(api).indexOf('::ffff:') !== -1) {
                api = api.substring(7);
            } else if (!api) {
                api = null;
            }
            return api;
        }

        /**
         * 输出图片
         * @param {string} pathstr 绝对路径
         * @returns 
         */
        image(pathstr) {
            this.#ctx.set(filehead(pathstr));
            return fs.readFileSync(pathstr);
        }

        /**
         * 输出视频
         * @param {string} pathstr 绝对路径
         * @returns 
         */
        video(pathstr) {
            this.#ctx.set(filehead(pathstr));
            return readFile(pathstr);
        }

        /**
         * 输出视图
         * @param {string} path 
         * @param {object} param1 
         */
        view(view, options = {}) {
            let { data, methods } = options;
            let pathInfo = path.parse(view);
            let pathstr = path.resolve(__dirname, './views/', pathInfo.dir, pathInfo.name + (pathInfo.ext != '.vue' ? '.vue' : pathInfo.ext));
            return this.#ctx.render({
                path: pathstr,
                data: () =>({appTitle:'yoyo-koa',...data}),
                methods: methods || {}
            });
        }

        /**
         * 输出文件（下载）
         * @param {string} pathstr 绝对路径
         * @param {string?} fileName 文件名
         * @returns 
         */
        download(pathstr, fileName = null) {
            const fileStream = fs.createReadStream(pathstr);
            const fileSize = fs.statSync(pathstr).size;
            if (!fileName) {
                fileName = path.parse(pathstr).base;
            }
            // 设置响应头
            this.#ctx.set({
                'Content-disposition': `attachment; filename=${encodeURIComponent(fileName)}`,
                'Content-type': 'application/octet-stream',
                'Content-length': fileSize,
            });
            return fileStream;
        }


        /**
         * 上传文件
         * @param {String} key 文件上传字段名
         * @param {Boolean} randName 是否随机文件名 true
         * @param {Array} suffix 允许上传后缀 []
         * @param {Number} size 文件大小上限(字节) 209715200 ~ 200M
         * @param {String} path 上传的路径upload
         * @returns 
         */
        upload(key = 'file', randName = true, suffix = [], size = (1024 * 1024 * 200), paths = '') {
            let fileObj = this.#ctx.request.files[key];
            if (!fileObj) {
                return { status: false, msg: 'key not found' };
            }

            let fileNameInfo = path.parse(fileObj.name);
            if (suffix.length > 0) {
                let ext = path.extname(String(fileObj.name)).toLowerCase().replace(/^\.(.+)$/, '$1');
                let extArr = suffix.filter(v => String(v).toLowerCase() == ext);
                if (extArr.length <= 0) {
                    return { status: false, msg: 'The format does not match' };
                }
            }

            if (fileObj.size > Number(size)) {
                return { status: false, msg: 'File exceeds maximum ' + Number(size) + ' limit' };
            }

            if (randName) {
                fileNameInfo.name = this.md5(String(Math.random()) + Date.now());
            }
            let storageMdr = 'static/upload/' + this.getNowFormatDate() + '/';
            if (paths) {
                storageMdr = paths;//使用用户提供的目录
                if (!/.*\/$/.test(storageMdr)) {
                    storageMdr += "/";//结尾必须斜杠
                }
            }

            const reader = fs.createReadStream(fileObj.path);
            // 目录不存在创建
            if (!fs.existsSync(storageMdr)) {
                fs.mkdirSync(storageMdr);
            }
            let storagePath = storageMdr + fileNameInfo.name + String(fileNameInfo.ext).toLowerCase();

            const upStream = fs.createWriteStream(storagePath);
            reader.pipe(upStream);
            return { status: true, msg: 'successfully upload', path: storagePath.replace(/^static(.+)$/, '$1') };
        }

        

    }

   

    global.fun = new functions;//不附带ctx的函数
    return async (ctx, next) => {
        ctx.fun = new ctxBase(ctx);//附带了ctx的函数
        await next();
    }
}